<?php

namespace Cekurte\Cli\Command;
 
use Symfony\Component\Console\Input\InputArgument,
    Symfony\Component\Console\Input\InputOption,
    Symfony\Component\Console\Command\Command,
    Symfony\Component\Console\Input\InputInterface,
    Symfony\Component\Console\Output\OutputInterface,
    Symfony\Component\Console\Input\ArrayInput;
 
/**
 * Classe responsável por executar todo o tratamento necessário
 * aos arquivos YAML gerados pelo doctrine 2 com base no banco de dados
 *
 * @author 	João Paulo Cercal
 * @version 1.0
 */
class Yaml extends Command
{
	/**
	 * @var stdClass Armazena os argumentos informados pelo usuário
	 */
	private $options 	= null;
	
	/**
	 * @var array Armazena os diretórios disponíveis para manipulação
	 */
	private $path		= null;
	
	/**
	 * Método construtor
	 * 
	 * Configura e aplica as permissões nos diretórios disponíveis 
	 * 
	 * @author 	João Paulo Cercal
	 * @version 1.0
	 */
	public function __construct()
	{
		parent::__construct();
		
		$this->path = array(
			'local'		=> realpath( dirname( __FILE__ ) ),
			'yaml'		=> realpath( APPLICATION_PATH . '/data/doctrine/yaml' ),
			'yaml-db'	=> realpath( APPLICATION_PATH . '/data/doctrine/yaml-db' ),
			'entities'	=> realpath( APPLICATION_PATH . '/Entities' ),
			'proxies'	=> realpath( APPLICATION_PATH . '/Proxies' ),
		);
		
		$this->setPermissao(0777);
	}
	
	/**
	 * Configura as permissões aos diretórios que serão utilizados pela aplicação
	 * 
	 * @param 	int $permissao um número octal contendo as permissões desejadas, recomenda-se 0777
	 * 
	 * @author 	João Paulo Cercal
	 * @version 1.0
	 */
	private function setPermissao( $permissao )
    {
	    foreach( $this->path as $dir )
		{
			if( @chmod($dir, $permissao) === false )
			{
				echo 'Ocorreu um erro ao aplicar a permissao no diretorio "' . $dir . "'!" . PHP_EOL;
			}
		}
    }
	
    /**
     * Disponibiliza na propriedade privada $this->options desta classe os argumentos informados pelo usuário
     * 
     * @param 	InputInterface $input
     * @param 	array|object $options contendo valores referentes as chaves da propriedade privada $this->path desta classe
     * 
     * @author 	João Paulo Cercal
     * @version 1.0
     */
	private function setOptions( $input, $options )
    {
    	$this->options = new \stdClass();
    	
    	foreach( $options as $option )
    	{
    		$this->options->{$option} = $input->getOption($option);
    	}
    }
    
	/**
     * Apaga os arquivos de um diretório
     * 
     * @param 	string $dir nome do diretório, deve ser uma chave presente na propriedade privada desta classe "$this->path"
     * 
     * @author 	João Paulo Cercal
     * @version 1.0
     * 
     * @return 	string mensagem
     */
    private function delete( $dir )
    {
		foreach (new \DirectoryIterator($this->path[$dir]) as $file)
		{
			if( !$file->isDot() and !$file->isDir() )
			{
				if( @unlink( realpath( $this->path[$dir] . '/' . $file->getFilename() )  ) === false )
				{
					return 'Ocorreu um problema ao remover o arquivo "' . $file->getFilename() . "'!";
				}
			}
		}
		
		return 'Os arquivos do diretorio "' . $dir . '" foram removidos com sucesso!';

    }

    /**
     * Configura os arquivos do diretório "yaml-db" com o namespace informado
     * 
     * @param 	string $namespace
     * 
     * @author 	João Paulo Cercal
     * @version 1.0
     * 
     * @return 	string mensagem
     */
	private function setNamespace( $namespace )
	{
		$dir 	= 'yaml-db';
		$dirTmp = 'tmp';
		
		// -------------------------------------------------------------------------
		// Se o diretório temporário não existir, ele é criado com permissões 0777
		
		if( !file_exists( $this->path[$dir] . '/' . $dirTmp ) )
		{
			mkdir( $this->path[$dir] . '/' . $dirTmp  );
		}
		
		// -------------------------------------------------------------------------
		// Cria os arquivos com base no namespace especificado no diretório temporário
		
		foreach (new \DirectoryIterator($this->path[$dir]) as $file)
		{
			if( !$file->isDot() and !$file->isDir() )
			{
				$tamanhoNamespace = strlen($namespace);
				
				// Verifica se o arquivo já foi atualizado para o namespace solicitado..
				if( substr( $file->getFilename() , 0, $tamanhoNamespace) != $namespace )
				{
					$origem 	= $this->path[$dir] . "/{$file->getFilename()}";
					$destino 	= $this->path[$dir] . "/{$dirTmp}/{$namespace}.{$file->getFilename()}";
					
					if( @rename ( $origem, $destino ) === false )
					{
						// Remove o diretório temporário
						rmdir( $this->path[$dir] . "/{$dirTmp}" );
						
						return 'Ocorreu um problema ao renomear o arquivo "' . $file->getFilename() . "'!";
					}
					else
					{
						// Abre o arquivo e transfere todo seu conteudo, 
						// em forma de array para a variavel "$conteudoArquivo"
						$conteudoArquivo 	= file( $destino );
					
						// Armazena o namespace configurado..
						$conteudoArquivo[0] = $namespace . '\\' . $conteudoArquivo[0];
						
						// Insere o namespace configurado no arquivo..
						file_put_contents( $destino, $conteudoArquivo );
					}
				}
				else
				{
					// Remove o diretório temporário
					rmdir( $this->path[$dir] . "/{$dirTmp}" );
					
					return 'O namespace "' . $namespace . '" ja esta sendo utilizado no arquivo "' . $file->getFilename() . '"!';
				}
			}
		}
		
		// -------------------------------------------------------------------------
		// Transfere os arquivos do diretórios temporário, para o diretório "yaml-db"
		
		$path = $this->path[$dir] . "/{$dirTmp}";
		
		foreach (new \DirectoryIterator( $path ) as $file)
		{
			if( !$file->isDot() and !$file->isDir() )
			{
				$origem 	= $this->path[$dir] . "/{$dirTmp}/{$file->getFilename()}";
				$destino 	= $this->path[$dir] . "/{$file->getFilename()}";
				
				rename ( $origem, $destino );
			}
		}
		
		// -------------------------------------------------------------------------
		// Remove o diretório temporário
		
		rmdir( $this->path[$dir] . "/{$dirTmp}" );
		
		return 'Os arquivos do diretorio "' . $dir . '" foram editados com sucesso!';
	}
	
	/**
     * Move os arquivos de um diretório para outro
     * 
     * @param string $origem nome do diretório de origem, é o diretório do qual os arquivos serão recortados, deve ser uma chave presente na propriedade privada desta classe "$this->path"
     * @param string $destino nome do diretório alvo, é o diretório onde os arquivos serão colados, deve ser uma chave presente na propriedade privada desta classe "$this->path"
     * 
     * @author João Paulo Cercal
     * @version 1.0
     * 
     * @return string mensagem
     */
	private function moveFiles( $origem, $destino )
	{
		foreach (new \DirectoryIterator($this->path[$origem]) as $file)
		{
			if( !$file->isDot() and !$file->isDir() )
			{
				$origemAux 		= $this->path[$origem]  . "/{$file->getFilename()}";
				$destinoAux 	= $this->path[$destino] . "/{$file->getFilename()}";

				if( @rename ( $origemAux, $destinoAux ) === false )
				{
					return "Ocorreu um problema ao mover o arquivo '{$file->getFilename()}', do diretorio '{$origem}' para o diretorio '{$destino}'!";
				}
			}
		}
		
		return "Os arquivos do diretorio '{$origem}' foram movidos para o diretorio '{$destino}' com sucesso!";
	}
	
	/**
     * Cria os arquivos Yaml tendo como base o banco de dados
     * 
     * @param string $destino nome do diretório alvo, é o diretório onde os arquivos "yaml" serão criados, deve ser uma chave presente na propriedade privada desta classe "$this->path"
     * @param OutputInterface $output
     * 
     * @author João Paulo Cercal
     * @version 1.0
     */
	private function createYamlFromDatabase( $destino, $output )
	{
		$comando = $this->getApplication()->find('orm:convert-mapping');

	    $argumentos = array(
	        'command' 			=> 'orm:convert-mapping',
	       	'to-type'    		=> 'yaml',
	    	'dest-path'    		=> $this->path[$destino],
	        '--force'  			=> true,
	    	'--from-database'  	=> true,
	    );
	
	    $input = new ArrayInput($argumentos);
	    
	    $comando->run($input, $output);
	}
    
	/**
	 * Cria "Entities" com base nos arquivos "yml" do diretorio "yaml" e "Proxies" com base nas "Entities" recém criadas
	 * 
	 * @param string $destino nome do diretório alvo, é o diretório onde as "Entities" serão criadas, deve ser uma chave presente na propriedade privada desta classe "$this->path"
     * @param OutputInterface $output
     * 
     * @author João Paulo Cercal
     * @version 1.0
     */
	private function createEntitiesProxiesFromYaml( $destino, $output )
	{
		// -------------------------------------------------------------------------
		// Generate Entities
		
		$comando = $this->getApplication()->find('orm:generate-entities');

	    $argumentos = array(
	        'command' 			=> 'orm:generate-entities',
	    	'dest-path'    		=> realpath( $this->path[$destino] . '/../' ),
	    );
	
	    $input = new ArrayInput($argumentos);
	    
	    $comando->run($input, $output);
	    
	    // -------------------------------------------------------------------------
		// Generate Proxies
		
		$comando = $this->getApplication()->find('orm:generate-proxies');

	    $argumentos = array(
	        'command' 			=> 'orm:generate-proxies',
	    );
	
	    $input = new ArrayInput($argumentos);
	    
	    $comando->run($input, $output);
	}
	
	
    protected function configure() 
    {
    	$this->setName('cekurte:yaml')
        ->setDescription('Trata os arquivos yaml gerados pelo doctrine 2.')
        ->setDefinition( array(
        
            new InputOption('delete', null, InputOption::VALUE_REQUIRED, 
            	"Deleta todo o conteudo de um diretorio. Os diretorios disponiveis sao: 'local', 'yaml', 'yaml-db', 'entities', 'proxies'."),
            
            new InputOption('namespace', null, InputOption::VALUE_REQUIRED, 
            	'Namespace que sera utilizado, configura os arquivos que estao presente no diretorio "yaml-db".'),
            
            new InputOption('create-yaml-from-database', null, InputOption::VALUE_REQUIRED, 
            	'Cria os arquivos "yaml" a partir do banco e dados, recebe o diretorio em que os scripts "yaml" serao criados.'),
            
            new InputOption('create-entities-proxies-from-yaml', null, InputOption::VALUE_REQUIRED, 
            	'Cria "Entities" com base nos arquivos "yml" do diretorio "yaml" e "Proxies" com base nas "Entities" recem criadas, recebe o diretorio em que as "Entities" serao criadas.'),
            
            new InputOption('move-yaml-db-to-yaml', null, InputOption::VALUE_NONE, 
            	'Move os arquivos do diretorio "yaml-db" para o diretorio "yaml".'),
            
            
        ))
        ->setHelp(<<<EOT
Trata os arquivos yaml gerados pelo doctrine 2.
EOT
        );
    }
    
    protected function execute(InputInterface $input, OutputInterface $output) 
    {
    	// ---------------------------------------------------------
    	// Armazena as opções disponíveis
    	
    	$this->setOptions( $input, array(
    		'delete', 
    		'namespace', 
    		'move-yaml-db-to-yaml', 
    		'create-yaml-from-database',
    		'create-entities-proxies-from-yaml',
    	));
    	
    	// ---------------------------------------------------------
    	// Ex: --delete=yaml
    	
    	if( !empty( $this->options->delete ) )
    	{
    		$encontrado = false;
    		
    		foreach( $this->path as $key => $value )
    		{
    			if( $key == $this->options->delete )
    			{
    				$encontrado = true;
    				
    				$result = $this->delete( $key );
    			}
    		}
    		
    		if( $encontrado === false )
    		{
    			$result = 'Diretorio nao disponivel!';
    		}
    	}
    	
    	// ---------------------------------------------------------
    	// Ex: --namespace=Entities
    	
    	if( !empty( $this->options->namespace ) )
    	{
    		$result = $this->setNamespace( $this->options->namespace );
    	}
    	
    	// ---------------------------------------------------------
    	// Ex: --create-yaml-from-database=yaml-db
    	
    	if( !empty( $this->options->{'create-yaml-from-database'} ) )
    	{
    		$result = $this->createYamlFromDatabase( $this->options->{'create-yaml-from-database'}, $output );
    	}
    	
    	// ---------------------------------------------------------
    	// Ex: --create-entities-proxies-from-yaml
    	
    	if( !empty( $this->options->{'create-entities-proxies-from-yaml'} ) )
    	{
    	
    		$result = $this->createEntitiesProxiesFromYaml( $this->options->{'create-entities-proxies-from-yaml'}, $output );
    	}
    	
    	// ---------------------------------------------------------
    	// Ex: --move-yaml-db-to-yaml
    	
    	if( !empty( $this->options->{'move-yaml-db-to-yaml'} ) )
    	{
    		$this->moveFiles('yaml-db', 'yaml');
    		
    		$result = '';
    	}
    	
    	// ---------------------------------------------------------
    	// Saída
        
        $output->write( PHP_EOL . $result . PHP_EOL);
    }
    
}